﻿using System;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace MadLibrary.IO
{
    /// <summary>
    /// Contains file-system utilities.
    /// </summary>
    public static class IOUtil
    {
        /// <summary>
        /// Generates a byte array representation of the given object.
        /// </summary>
        /// <param name="obj">The object to serialize.</param>
        /// <returns>A System.Byte[] containing the data of the specified object.</returns>
        public static byte[] GenerateBytes(this Object obj)
        {
            if (obj == null) return new byte[0];
            MemoryStream stream = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));

            bf.Serialize(stream, obj);
            stream.Seek(0, SeekOrigin.Begin);
            byte[] bytes = stream.ToArray();
            stream.Dispose();
            return bytes;
        }

        /// <summary>
        /// Renames all files in the specified directory using the given format string and counter.
        /// </summary>
        /// <param name="directoryPath">The directory containing the files to rename.</param>
        /// <param name="formatString">The string to format with.</param>
        /// <param name="counter">The counter to use with the format string. Can be left null.</param>
        public static void MassChangeFileName(string directoryPath, string formatString, Counter counter)
        {
            MassChangeFileName(Directory.EnumerateFiles(directoryPath).ToArray(), formatString, counter);
        }

        /// <summary>
        /// Renames the the specified files using the given format string and counter.
        /// </summary>
        /// <param name="files">The files to rename.</param>
        /// <param name="formatString">The string to format with.</param>
        /// <param name="counter">The counter to use with the format string. Can be left null.</param>
        public static void MassChangeFileName(string[] files, string formatString, Counter counter = null)
        {
            if (files == null || formatString == null)
                throw new ArgumentNullException();
            if (counter == null)
                counter = new OneBasedCounter();
            if (!formatString.Contains("{0}"))
                formatString += "{0}";
            foreach (string path in files)
            {
                string extension = Path.GetExtension(path);
                string directory = path.Substring(0, path.Length - Path.GetFileName(path).Length);
                string count = counter.Next();
                File.Move(path, directory + string.Format(formatString, count) + extension);
            }
        }

        /// <summary>
        /// Gets the path of the user folder.
        /// </summary>
        public static string UserFolder
        {
            get
            {
                return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
            }
        }

        /// <summary>
        /// Represents an abstract counter.
        /// </summary>
        public abstract class Counter
        {
            /// <summary>
            /// When implemented in derived class, returns a consistent or inconsistent value
            /// used for counting.
            /// </summary>
            public abstract string Next();
        }

        /// <summary>
        /// A counter that starts at zero and goes up by one each count.
        /// </summary>
        public class ZeroBasedCounter : Counter
        {
            private int i = 0;

            /// <summary>Gets the next.</summary>
            /// <returns>.</returns>
            public override string Next()
            {
                return (i++).ToString();
            }
        }

        /// <summary>
        /// A counter that starts at one and goes up by one each count.
        /// </summary>
        public class OneBasedCounter : Counter
        {
            private int i = 0;

            /// <summary>Gets the next.</summary>
            /// <returns>.</returns>
            public override string Next()
            {
                return (++i).ToString();
            }
        }

        /// <summary>
        /// A counter that returns a random number.
        /// </summary>
        public class RandomNumberCounter : Counter
        {
            private Random rand = new Random();

            /// <summary>Gets the next.</summary>
            /// <returns>.</returns>
            public override string Next()
            {
                return (rand.Next()).ToString();
            }
        }

        /// <summary>
        /// A counter that returns a random string.
        /// </summary>
        public class RandomTextCounter : Counter
        {
            private Random rand = new Random();
            private int length;

            /// <summary>
            /// Initializes a new instance of the RandomTextCounter class.
            /// </summary>
            /// <param name="length">The length.</param>
            public RandomTextCounter(int length)
            {
                this.length = length;
            }

            /// <summary>
            /// Next
            /// </summary>
            /// <returns></returns>
            public override string Next()
            {
                return Text.RandomText.Generate(Text.SupportedCharacters.AlphaNumeric, Text.CharacterCase.Alternating, length);
            }
        }
    }
}